﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MugenMvvmToolkit.Attributes;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit.Utils
{
    internal static partial class MvvmUtilsInternalAdvanced
    {
        #region Fields

        private static readonly Dictionary<MemberInfo, Attribute[]> CachedAttributes =
            new Dictionary<MemberInfo, Attribute[]>();

        private static readonly Dictionary<Type, IList<string>> CachedIgnoreAttributes =
            new Dictionary<Type, IList<string>>();

        private static readonly Dictionary<Type, IDictionary<string, ICollection<string>>> CachedViewModelProperties =
            new Dictionary<Type, IDictionary<string, ICollection<string>>>();

        private static readonly IList<string> ExcludedProperties = typeof(EditableViewModel<>)
            .GetProperties()
            .Select(info => info.Name)
            .ToArray();

        #endregion

        #region Methods

        internal static IDictionary<string, ICollection<string>> GetViewModelToModelProperties(Type type)
        {
            using (DeadLockDetector.Lock(CachedViewModelProperties))
            {
                IDictionary<string, ICollection<string>> result;
                if (!CachedViewModelProperties.TryGetValue(type, out result))
                {
                    result = new Dictionary<string, ICollection<string>>();
                    foreach (PropertyInfo propertyInfo in type.GetProperties())
                    {
                        IEnumerable<ModelPropertyAttribute> attributes =
                            GetAttributes(propertyInfo).OfType<ModelPropertyAttribute>();
                        foreach (ModelPropertyAttribute viewModelToModelAttribute in attributes)
                        {
                            ICollection<string> list;
                            if (!result.TryGetValue(viewModelToModelAttribute.Property, out list))
                            {
                                list = new HashSet<string>();
                                result[viewModelToModelAttribute.Property] = list;
                            }
                            list.Add(propertyInfo.Name);
                        }
                    }
                    CachedViewModelProperties[type] = result;
                }
                return result.ToDictionary(pair => pair.Key, pair => pair.Value);
            }
        }

        internal static IList<string> GetIgnoreProperties(Type type)
        {
            using (DeadLockDetector.Lock(CachedIgnoreAttributes))
            {
                IList<string> result;
                if (!CachedIgnoreAttributes.TryGetValue(type, out result))
                {
                    result = new List<string>(ExcludedProperties);
                    foreach (PropertyInfo propertyInfo in type.GetProperties())
                    {
                        if (propertyInfo.IsDefined(typeof(IgnorePropertyAttribute), true))
                            result.Add(propertyInfo.Name);
                    }
                    CachedIgnoreAttributes[type] = result.ToArray();
                }
                return result;
            }
        }

        internal static T GetAttribute<T>(this MemberInfo memberInfo) where T : Attribute
        {
            Attribute[] attributes = GetAttributes(memberInfo);
            for (int i = 0; i < attributes.Length; i++)
            {
                Attribute attribute = attributes[i];
                var customAttribute = attribute as T;
                if (customAttribute == null) continue;
                return customAttribute;
            }
            return null;
        }

        internal static Attribute[] GetAttributes(this MemberInfo memberInfo)
        {
            Attribute[] attributes;
            using (DeadLockDetector.Lock(CachedAttributes))
            {
                CachedAttributes.TryGetValue(memberInfo, out attributes);
                if (attributes == null)
                {
#if !NETFX_CORE
                    object[] customAttributes = memberInfo.GetCustomAttributes(typeof(Attribute), true);
#else
                    object[] customAttributes = memberInfo.GetCustomAttributes(typeof(Attribute), true).ToArray();
#endif
                    attributes = new Attribute[customAttributes.Length];
                    for (int i = 0; i < customAttributes.Length; i++)
                    {
                        attributes[i] = (Attribute)customAttributes[i];
                    }
                    CachedAttributes[memberInfo] = attributes;
                }
            }
            return attributes;
        }

#if WPF
        internal static bool CanInitializeView(IInitializationContext context)
        {
            var hasView = context.ViewModel as IHasView;
            return hasView != null && !context.ActivationPolicy.WithoutView && !(hasView is NavigableViewModel);
        }
#endif

        #endregion
    }
}